package com.cmw.socket.l7.server;


import com.cmw.socket.constant.UDPConstants;
import com.cmw.socket.utils.ByteUtils;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.SocketException;
import java.nio.ByteBuffer;
import java.util.UUID;

/**
 * UDP提供类
 * @author chengmingwei
 * @date 2020-06-26 11:04
 */
@Slf4j
public class UDPProvider {

    private static Provider PROVIDER_INSTANCE;

    static void start(int port){
        stop();
        String sn = UUID.randomUUID().toString();
        log.info("sn={}",sn);
        Provider provider = new Provider(sn, port);
        provider.start();
        PROVIDER_INSTANCE = provider;
    }

    static void stop(){
        if(null == PROVIDER_INSTANCE) return;
        PROVIDER_INSTANCE.exit();
        PROVIDER_INSTANCE = null;
    }

    private static class Provider extends Thread{
        private final byte[] sn;
        private final int port;
        private boolean done = false;
        private DatagramSocket ds = null;
        //存储消息的Buffer
        final byte[] buffer = new byte[128];

        Provider(String sn, int port){
            super();
            this.sn = sn.getBytes();
            this.port = port;
        }

        @Override
        public void run() {
            super.run();
            try {
                //监听 30201 端口
                ds = new DatagramSocket(UDPConstants.PORT_SERVER);
                //接收消息的 Packet
                DatagramPacket receivePack = new DatagramPacket(buffer, buffer.length);
                while (!done){
                    //接收报文数据
                    ds.receive(receivePack);
                    //打印接收到的信息与发送者的信息
                    //发送者的IP地址
                    String clientIp = receivePack.getAddress().getHostAddress();
                    int clientPort = receivePack.getPort();
                    int clientDataLen = receivePack.getLength();
                    byte[] clientData = receivePack.getData();
                    boolean isValid = clientDataLen >= (UDPConstants.HEADER.length+2+4) && (ByteUtils.startsWith(clientData, UDPConstants.HEADER));
                    log.info("UDPProvider receive form ip:" + clientIp
                            + "\tport:" + clientPort + "\tdataValid:" + isValid);
                    if(!isValid){ //验证无效继续
                        log.error("接收到的数据不合法，请检查！");
                        continue;
                    }
                    //解析命令与回送端口
                    int index = UDPConstants.HEADER.length;
                    short cmd = (short)((clientData[index++] << 8) | (clientData[index++] & 0xff));

                    int responsePort = (
                            ((clientData[index++]) << 24) |
                                    ((clientData[index++] & 0xff) << 16) |
                                    ((clientData[index++] & 0xff) << 8) |
                                    ((clientData[index] & 0xff))
                    );

                    // 判断合法性
                    if(cmd == 1 && responsePort > 0){
                        //构建一份回送数据
                        ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
                        byteBuffer.put(UDPConstants.HEADER);
                        byteBuffer.putShort((short)2);
                        byteBuffer.putInt(port); //30401
                        byteBuffer.put(sn);
                        int len = byteBuffer.position();
                        //直接根据发送者构建一份回送信息
                        DatagramPacket responsePacket = new DatagramPacket(buffer, len, receivePack.getAddress(), responsePort);
                        ds.send(responsePacket);
                        log.info(String.format("UDPProvider response to: %s \t port:%d \t dataLen:%d", clientIp, responsePort, len));
                    }else{
                        log.error("UDPProvider receive cmd nonsupport; cmd :{}, \t port: {}", cmd, port);
                    }
                }
            } catch (Exception ignored) {

            }finally {
                close();
            }
            // 完成
            System.out.println("UDPProvider Finished.");
        }

        private void close(){
            if(null == ds) return;
            ds.close();
            ds = null;
        }

        /**
         * 提供结束
         */
        private void exit(){
            done = true;
            close();
        }

    }


}
